using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using Triton.Bot;

namespace Monitor
{
    [ServiceContract(CallbackContract = typeof(IMonitorCallback))]
    public interface IMonitorService
    {
        [OperationContract]
        void Connect();
        [OperationContract]
        ProxyStats GetStats();
        [OperationContract]
        void StartBot();
        [OperationContract]
        bool IsRunning();
    }

    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
    class MonitorService : IMonitorService
    {
        ILog Log;
        ProxyStats _stats;
        OperationContext _context;
        IMonitorCallback _callback;
        Action _startBotAction;
        
        public MonitorService(ILog log, Action startBotAction)
        {
            Log = log;
            _stats = new ProxyStats();
            _startBotAction = startBotAction;
        }

        public void Connect()
        {
            CloseAsnyc(this._context);
            try
            {
                this._context = OperationContext.Current;
                this._callback = this._context.GetCallbackChannel<IMonitorCallback>();
                Task.Factory.StartNew(() =>
                {
                    try
                    {
                        this._callback.OnConnected();
                    }
                    catch (Exception ex)
                    {
                        Log.WarnFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
                    }
                }, TaskCreationOptions.LongRunning);
            }
            catch (Exception ex)
            {
                Log.WarnFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
            }
        }

        public void SetStats(ProxyStats stats)
        {
            _stats = stats;
        }

        public ProxyStats GetStats()
        {
            return _stats;
        }

        public void StartBot()
        {
            Task.Factory.StartNew(_startBotAction.Invoke, TaskCreationOptions.LongRunning);
        }

        public void OnStartNewGame()
        {
            Task.Factory.StartNew(() =>
            {
                try
                {
                    if (_callback == null)
                        return;
                    this._callback.OnStartNewGame();
                }
                catch (Exception ex)
                {
                    Log.WarnFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
                }
            }, TaskCreationOptions.LongRunning);
        }

        public void OnGameOver(ProxyGameOverEventArgs e)
        {
            Task.Factory.StartNew(() =>
            {
                try
                {
                    if (_callback == null)
                        return;
                    this._callback.OnGameOver(e);
                }
                catch (Exception ex)
                {
                    Log.WarnFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
                }
            }, TaskCreationOptions.LongRunning);
        }

        public void OnStatsChanged(ProxyStats stats)
        {
            this._stats = stats;
            Task.Factory.StartNew(() =>
            {
                try
                {
                    if (_callback == null)
                        return;
                    this._callback.OnStatsChanged(stats);
                }
                catch (Exception ex)
                {
                    Log.WarnFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
                }
            }, TaskCreationOptions.LongRunning);
        }

        public void OnMulliganConfirm()
        {
            Task.Factory.StartNew(() =>
            {
                try
                {
                    if (_callback == null)
                        return;
                    this._callback.OnMulliganConfirm();
                }
                catch (Exception ex)
                {
                    Log.WarnFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
                }
            }, TaskCreationOptions.LongRunning);
        }

        public void OnBotStoped()
        {
            Task.Factory.StartNew(() =>
            {
                try
                {
                    if (_callback == null)
                        return;
                    this._callback.OnBotStoped();
                }
                catch (Exception ex)
                {
                    Log.WarnFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
                }
            }, TaskCreationOptions.LongRunning);
        }

        public void OnFatalError()
        {
            Task.Factory.StartNew(() =>
            {
                try
                {
                    if (_callback == null)
                        return;
                    this._callback.OnFatalError();
                }
                catch (Exception ex)
                {
                    Log.WarnFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
                }
            }, TaskCreationOptions.LongRunning);
        }

        void CloseAsnyc(OperationContext context)
        {
            if (context == null)
                return;
            Task.Factory.StartNew(obj =>
            {
                var c = obj as OperationContext;
                try
                {
                    c.Channel.Close();
                }
                catch
                {
                    c.Channel.Abort();
                }
            }, context, TaskCreationOptions.LongRunning);
        }

        public bool IsRunning()
        {
            try
            {
                return BotManager.IsRunning;
            }
            catch
            {
                return false;
            }
        }
    }

    [ServiceContract]
    public interface IMonitorCallback
    {
        [OperationContract]
        void OnConnected();
        [OperationContract]
        void OnGameOver(ProxyGameOverEventArgs e);
        [OperationContract]
        void OnStartNewGame();
        [OperationContract]
        void OnStatsChanged(ProxyStats stats);
        [OperationContract]
        void OnMulliganConfirm();
        [OperationContract]
        void OnBotStoped();
        [OperationContract]
        void OnFatalError();
    }

    [DataContract]
    public class ProxyStats
    {
        [DataMember]
        public int Wins;
        [DataMember]
        public int Losses;
        [DataMember]
        public int Concedes;
        [DataMember]
        public int Level;
        [DataMember]
        public int Xp;
        [DataMember]
        public int XpNeeded;
        [DataMember]
        public ProxySceneMode SceneMode;
        [DataMember]
        public ProxyGameState GameState;
    }

    [DataContract]
    public enum ProxyGameState
    {
        [EnumMember]
        None = 0,
        [EnumMember]
        StartingNewGame = 1,
        [EnumMember]
        Mulligan = 2,
        [EnumMember]
        GameOver = 3
    }

    [DataContract]
    public enum ProxySceneMode
    {
        [EnumMember]
        INVALID = 0,
        [EnumMember]
        STARTUP = 1,
        [EnumMember]
        LOGIN = 2,
        [EnumMember]
        HUB = 3,
        [EnumMember]
        GAMEPLAY = 4,
        [EnumMember]
        COLLECTIONMANAGER = 5,
        [EnumMember]
        PACKOPENING = 6,
        [EnumMember]
        TOURNAMENT = 7,
        [EnumMember]
        FRIENDLY = 8,
        [EnumMember]
        FATAL_ERROR = 9,
        [EnumMember]
        DRAFT = 10,
        [EnumMember]
        CREDITS = 11,
        [EnumMember]
        RESET = 12,
        [EnumMember]
        ADVENTURE = 13,
        [EnumMember]
        TAVERN_BRAWL = 14
    }

    [DataContract]
    public enum ProxyGameOverFlag
    {
        [EnumMember]
        Victory = 0,
        [EnumMember]
        Defeat = 1
    }

    [DataContract]
    public class ProxyGameOverEventArgs
    {
        [DataMember]
        public ProxyGameOverFlag Result;
        [DataMember]
        public bool Conceded;
    }
}
